// ++++++++++++++++++++++++++++++++++++++++
// 《零基础Go语言算法实战》源码
// ++++++++++++++++++++++++++++++++++++++++
// Author:廖显东（ShirDon）
// Blog:https://www.shirdon.com/
// Gitee:https://gitee.com/shirdonl/goAlgorithms.git
// Buy link :https://item.jd.com/14101229.html
// ++++++++++++++++++++++++++++++++++++++++

package main

import (
	"fmt"
)

// 包含 SVM 模型参数的结构体
type SVM struct {
	weights                 []float64 // SVM 模型的权重
	bias                    float64   // SVM 模型的偏差
	learningRate            float64   // 学习率
	regularizationParameter float64   // 正则化参数
}

// train()方法，用于在输入数据和标签上训练 SVM 模型
func (svm *SVM) train(X [][]float64, y []int, number int) {
	// 获取特征和样本的数量
	numFeatures := len(X[0])
	numSamples := len(X)

	// 将权重初始化为零
	svm.weights = make([]float64, numFeatures)
	for i := range svm.weights {
		svm.weights[i] = 0
	}

	// 将偏差初始化为零
	svm.bias = 0

	// 指定数量为 number 的训练模型
	for num := 0; num < number; num++ {
		// 遍历样本
		for i := 0; i < numSamples; i++ {
			// 计算激活
			activation := svm.bias
			for j := 0; j < numFeatures; j++ {
				activation += svm.weights[j] * X[i][j]
			}

			// 如果样本被错误分类，则更新模型参数
			if float64(y[i])*activation < 1 {
				for j := 0; j < numFeatures; j++ {
					svm.weights[j] += svm.learningRate *
						(float64(y[i])*X[i][j] - svm.regularizationParameter*svm.weights[j])
				}
				svm.bias += svm.learningRate * float64(y[i])
			} else {
				// 基于正则化更新模型参数
				for j := 0; j < numFeatures; j++ {
					svm.weights[j] += svm.learningRate *
						(-svm.regularizationParameter * svm.weights[j])
				}
			}
		}
	}
}

// 预测输入数据的标签
func (svm *SVM) predict(X []float64) int {
	// 计算激活数
	activation := svm.bias
	for j := 0; j < len(svm.weights); j++ {
		activation += svm.weights[j] * X[j]
	}

	// 返回预测标签
	if activation > 0 {
		return 1
	} else {
		return -1
	}
}

func main() {
	X := [][]float64{{1, 2}, {2, 3}, {3, 1}, {4, 3}, {5, 5}, {6, 6}, {7, 7}, {8, 8}}
	y := []int{-1, -1, -1, -1, 1, 1, 1, 1}

	// 初始化支持向量机模型
	svm := SVM{
		learningRate:            0.1,
		regularizationParameter: 0.1,
	}

	svm.train(X, y, 1000)

	fmt.Println(svm.predict([]float64{2, 2}))
	fmt.Println(svm.predict([]float64{6, 5}))
}

//$ go run sVM.go
//-1
//1
